package com.connection;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Vector;

/**
 * @author xinwang
 * @date 2020/3/17 22:12
 **/
public class ConnectionPool implements IConnectionPool {
    //线程安全  空闲线程容器
    private List<Connection> freeConnection = new Vector<>();
    //线程安全 活动线程容器
    private List<Connection> activeConnection = new Vector<>();
    //配置文件信息对象
    private DbBean dbBean;
    //标识
    private int count;

    public ConnectionPool(DbBean dbBean)  {
        //获取配置文件信息
        this.dbBean = dbBean;
        //调用初始化线程池方法
        try {
            init();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //1.初始化线程池（空闲线程）
    private void init() throws Exception {
        if (dbBean == null) {
            throw new Exception("配置文件信息获取失败");
        }
        //1.获取初始连接数
        for (int i = 0; i < dbBean.getInitConnections(); i++) {
            //2. 调用创建Connection连接方法
            Connection connection = newConnection();
            if (connection != null) {
                //3. 存放在空闲线程容器中 freeConnection
                freeConnection.add(connection);
            }
        }
    }

    /**
     * 创建Connection连接
     */
    private synchronized Connection newConnection() {
        try {
            Class.forName(dbBean.getDriverName());
            Connection connection = DriverManager.getConnection(dbBean.getUrl(), dbBean.getUserName(), dbBean.getPassword());
            //每创建一个连接 计数器加一
            count++;
            return connection;
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * 获取Connection连接 方法
     *
     * @return
     */
    public synchronized Connection getConnection() {
        Connection connection = null;
        try {
            //当前获取连接数 小于最大允许的连接数 去空闲线程池取出连接数
            if (count < dbBean.getMaxActiveConnections()) {
                //判断空闲线程池是否有数据
                if (freeConnection != null && !freeConnection.isEmpty()) {
                    //拿到在删除 freeConnection.get(0);   freeConnection.remove(0);
                    //从空闲线程中拿到连接 然后在移除掉
                    connection = freeConnection.remove(0);
                } else {
                    //如果为空 就创建新的连接
                    connection = newConnection();
                }
                //判断获取到连接是否可用
                boolean available = isAvailable(connection);
                if (available) {
                    //连接可用 存入活动线程池中
                    activeConnection.add(connection);
                } else {
                    //如果连接不可用 重试 递归重新获取
                    count--;
                    connection = getConnection();
                }
            } else {
                //当前获取连接数 大于最大允许的连接数 进行等待
                wait(dbBean.getConnTimeOut());
                //重试获取连接
                connection = getConnection();
            }
            return connection;
        } catch (Exception e) {
            return null;
        }

    }

    /**
     * 判断连接是否可用
     *
     * @param connection
     * @return
     */
    public boolean isAvailable(Connection connection) {
        try {
            if (connection == null || connection.isClosed()) {
                return false;
            }
        } catch (SQLException e) {

        }
        return true;
    }

    /**
     * 释放连接  回收
     *
     * @return
     */
    public synchronized Connection releaseConnection(Connection connection) {
        try {
            //1.判断连接是否可用
            if (isAvailable(connection)) {
                //2.判断空闲线程是否已满
                if (freeConnection.size() < dbBean.getMaxConnections()) {
                    //未满 回收存入空闲线程池中
                    freeConnection.add(connection);
                } else {
                    //已满  关闭连接
                    connection.close();
                }
                //移除 活动线程中当连接
                activeConnection.remove(connection);
                count--;
                //唤醒还在等待重试的连接
                notifyAll();
            }
        } catch (Exception e) {

        }
        return null;
    }
}
